package de.dailab.graphworld.bean;

import de.dailab.graphworld.bean.utils.BeanUtils;
import de.dailab.graphworld.ontology.ContractNetProtocol;
import de.dailab.graphworld.ontology.Proposal;
import de.dailab.graphworld.ontology.Registration;
import de.dailab.graphworld.ontology.RegistrationRequest;
import de.dailab.graphworld.ontology.Reply;
import de.dailab.graphworld.ontology.ReplyType;
import de.dailab.graphworld.ontology.Task;
import de.dailab.jiactng.agentcore.comm.ICommunicationAddress;
import de.dailab.jiactng.agentcore.comm.message.JiacMessage;
import de.dailab.jiactng.agentcore.knowledge.IFact;

/**
 * Abstract implementation of a contractor role in the Contract Net Protocol.
 *
 * @author Kseniya
 */
public abstract class Contractor extends ContractNetProtocolMember {

    @Override
    public void consume(JiacMessage msg) {
        IFact payload = msg.getPayload();
        if (payload instanceof RegistrationRequest) {
            register(msg.getSender());
        } else if (payload instanceof Reply) {
            Reply reply = (Reply) payload;
            consumeReply(reply, msg.getSender());
        }
    }

    private void consumeReply(Reply reply, ICommunicationAddress sender) {
        // no communication until we are initialised
        if (!isInitialisedInWorld()) {
            return;
        }
        ReplyType type = reply.getType();
        Task task = reply.getTask();
        if (ContractNetProtocol.CALL_FOR_PROPOSALS.equals(type)) {
            if (canAcceptTask(task)) {
                propose(task, sender);
            } else {
                refuse(task, sender);
            }
        } else if (ContractNetProtocol.ACCEPT_PROPOSAL.equals(type)) {
            consumeAcceptance(task);
        } else if (ContractNetProtocol.REJECT_PROPOSAL.equals(type)) {
            consumeRejection(task);
        }
    }

    /**
     * Registration procedure.
     *
     * @param caller who called for registration (can be the manager)
     */
    private void register(ICommunicationAddress caller) {
        sendMsgPersonal(new Registration(), caller);
        memory.write(caller);
    }

    /**
     * Creates a proposal on given task and sends it to the requester of the
     * proposal.
     *
     * @param task for which the proposal is created
     * @param requester who will get the proposal
     */
    private void propose(Task task, ICommunicationAddress requester) {
        sendMsgPersonal(createProposal(task), requester);
    }

    /**
     * Creates a refusal on given task and sends it to the requester of the
     * proposal.
     *
     * @param task that this contractor is refusing to perform
     * @param requester who will get the refusal
     */
    protected void refuse(Task task, ICommunicationAddress requester) {
        sendMsgPersonal(BeanUtils.createRefusal(task), requester);
    }

    /**
     * Creates proposal for the given task.
     *
     * @param task to create a proposal for
     * @return created proposal
     */
    protected abstract Proposal createProposal(Task task);

    /**
     *
     * @param task to be accepted
     * @return true if the contractor has enough capacity to perform the task
     */
    protected abstract boolean canAcceptTask(Task task);

    /**
     * Handles the manager's acceptance of proposal this contractor suggested on
     * task.
     *
     * @param task that this contractor can perform now
     */
    protected abstract void consumeAcceptance(Task task);

    /**
     * Handles the manager's reject of proposal this contractor suggested on
     * task.
     *
     * @param task that is forbidden for this contractor now
     */
    protected abstract void consumeRejection(Task task);
}
